package cn.iocoder.yudao.module.product.controller.app.spu;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.*;
import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
import cn.iocoder.yudao.module.product.dal.dataobject.recommend.RecommendDO;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
import cn.iocoder.yudao.module.product.service.recommend.RecommendService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;

@Tag(name = "用户 APP - 商品 SPU")
@RestController
@RequestMapping("/product/spu")
@Validated
public class AppProductSpuController {

    @Resource
    private ProductSpuService productSpuService;
    @Resource
    private ProductSkuService productSkuService;
    @Resource
    private ProductCategoryService productCategoryService;
    @Resource
    private ProductBrowseHistoryService productBrowseHistoryService;
    @Resource
    private ProductPropertyService productPropertyService;
    @Resource
    private ProductPropertyValueService productPropertyValueService;
    @Resource
    private RecommendService recommendService;

    @GetMapping("/list-by-ids")
    @Operation(summary = "获得商品 SPU 列表")
    @Parameter(name = "ids", description = "编号列表", required = true)
    @PermitAll
    public CommonResult<List<AppProductSpuRespVO>> getSpuList(@RequestParam("ids") Set<Long> ids) {
        List<ProductSpuDO> list = productSpuService.getSpuList(ids);
        if (CollUtil.isEmpty(list)) {
            return success(Collections.emptyList());
        }

        // 拼接返回
        list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
        List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);
        return success(voList);
    }

    @GetMapping("/page")
    @Operation(summary = "获得商品 SPU 分页")
    @PermitAll
    public CommonResult<PageResult<AppProductSpuRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
        PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }

        // 拼接返回
        pageResult.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
        PageResult<AppProductSpuRespVO> voPageResult = BeanUtils.toBean(pageResult, AppProductSpuRespVO.class);
        return success(voPageResult);
    }

    @GetMapping("/get-detail")
    @Operation(summary = "获得商品 SPU 明细")
    @Parameter(name = "id", description = "编号", required = true)
    @PermitAll
    public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
        // 获得商品 SPU
        ProductSpuDO spu = productSpuService.getSpu(id);
        if (spu == null) {
            throw exception(SPU_NOT_EXISTS);
        }
        if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {
            throw exception(SPU_NOT_ENABLE, spu.getName());
        }
        // 获得商品 SKU
        List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());

        // 增加浏览量
        productSpuService.updateBrowseCount(id, 1);
        // 保存浏览记录
        productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id);

        // 拼接返回
        spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount());
        AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class)
                .setSkus(BeanUtils.toBean(skus, AppProductSpuDetailRespVO.Sku.class));
        return success(spuVO);
    }

    @PostMapping("/get-product-data")
    @Operation(summary = "获得商品数据")
    @PermitAll
    public CommonResult<AppProductSkuRespVO> getProductSku(@RequestBody AppProductSkuReqVO reqVO) {
        if (reqVO.getProperties() == null) {
            reqVO.setProperties(new ArrayList<>());
        }
        ProductSpuDO spu = null;
        Long productId = reqVO.getProductId();
        if (productId != null) {
            spu = productSpuService.getSpu(productId);
            if (spu == null) {
                throw exception(SPU_NOT_EXISTS);
            }
            reqVO.setSubCategoryId(spu.getCategoryId());
            ProductCategoryDO subCategory = productCategoryService.getCategory(spu.getCategoryId());
            reqVO.setCategoryId(subCategory.getParentId());
        }
        //一级分类
        List<ProductCategoryDO> topCategories = productCategoryService.getCategoriesByParentId(0L);
        if (CollUtil.isEmpty(topCategories)) {
            throw exception(SPU_NOT_EXISTS);
        }
        Long categoryId = reqVO.getCategoryId();
        if (categoryId == null) {
            categoryId= topCategories.get(0).getId();
        }
        //二级分类
        List<ProductCategoryDO> subCategories = productCategoryService.getCategoriesByParentId(categoryId);
        if (CollUtil.isEmpty(topCategories)) {
            throw exception(SPU_NOT_EXISTS);
        }
        Long subCategoryId = reqVO.getSubCategoryId();
        if (subCategoryId == null) {
            subCategoryId = subCategories.get(0).getId();
        }
        //SPU
        List<ProductSpuDO> products = productSpuService.getSpuListInCategory(subCategoryId);
        if (CollUtil.isEmpty(products)) {
            throw exception(SPU_NOT_EXISTS);
        }
        if (productId == null) {
            productId = products.get(0).getId();
        }
        for (ProductSpuDO product : products) {
            if (product.getId().equals(productId)) {
                spu = product;
                break;
            }
        }
        if (spu == null) {
            throw exception(SPU_NOT_EXISTS);
        }

        //其他实体列表
        List<ProductSkuDO> skuList = productSkuService.getSkuListBySpuId(spu.getId());
        List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList();
        List<ProductPropertyValueDO> propertyValueList = productPropertyValueService.getPropertyValueList();
        propertyList.sort(Comparator.comparing(ProductPropertyDO::getSort));

        //组装map方便下面使用
        Map<Long, ProductPropertyValueDO> propertyValueMap = new HashMap<>();
        for (ProductPropertyValueDO valueDO : propertyValueList) {
            propertyValueMap.put(valueDO.getId(), valueDO);
        }
        Map<Long, Long> selectedPropertyMap = new HashMap<>();
        for (AppProductSkuReqVO.ReqProperty reqProperty : reqVO.getProperties()) {
            selectedPropertyMap.put(reqProperty.getId(), reqProperty.getValueId());
        }

        //组装全量属性树
        List<AppProductSkuRespPropertyVO> allProperties = new ArrayList<>();
        for (ProductPropertyDO propertyDO : propertyList) {
            AppProductSkuRespPropertyVO propertyVO = AppProductSkuRespPropertyVO.fromPropertyDO(propertyDO);
            for (ProductPropertyValueDO valueDO : propertyValueList) {
                if (valueDO.getPropertyId().equals(propertyDO.getId())) {
                    propertyVO.getValues().add(AppProductSkuRespPropertyValueVO.fromProductPropertyValue(valueDO));
                }
            }
            allProperties.add(propertyVO);
        }

        //按属性顺序确定可选值
        //每个选中的属性值决定下一个属性的可选值列表
        List<AppProductSkuRespPropertyVO> availableProperties = new ArrayList<>();
        for (int i = 0; i<allProperties.size(); i++) {
            AppProductSkuRespPropertyVO currentProperty = allProperties.get(i);
            Map<Long, AppProductSkuRespPropertyValueVO> availableValueMap = new HashMap<>();
            if (availableProperties.isEmpty()) { //第一个属性，只要sku包含此属性，sku此属性值就是可选的
                for (ProductSkuDO sku : skuList) {
                    for (ProductSkuDO.Property skuProperty : sku.getProperties()) {
                        if (skuProperty.getPropertyId().equals(currentProperty.getId())) {
                            ProductPropertyValueDO valueDO = propertyValueMap.get(skuProperty.getValueId());
                            availableValueMap.put(skuProperty.getValueId(), AppProductSkuRespPropertyValueVO.fromProductPropertyValue(valueDO));
                        }
                    }
                }
            } else {
                AppProductSkuRespPropertyVO lastProperty = availableProperties.get(availableProperties.size()-1);
                Long lastValueId = selectedPropertyMap.get(lastProperty.getId());
                if (lastValueId == null) {
                    for (AppProductSkuRespPropertyValueVO valueVO : lastProperty.getValues()) {
                        if (valueVO.isDefault()) {
                            lastValueId = valueVO.getId();
                            break;
                        }
                    }
                }
                if (lastValueId == null) {
                    AppProductSkuRespPropertyValueVO lastPropertyFirstValueVo = lastProperty.getValues().get(0);
                    lastPropertyFirstValueVo.setDefault(true);
                    lastValueId = lastPropertyFirstValueVo.getId();
                }
                for (ProductSkuDO sku : skuList) {
                    for (ProductSkuDO.Property skuProperty : sku.getProperties()) {
                        if (skuProperty.getPropertyId().equals(lastProperty.getId()) && skuProperty.getValueId().equals(lastValueId)) {
                            for (ProductSkuDO.Property skuProperty2 : sku.getProperties()) {
                                if (skuProperty2.getPropertyId().equals(currentProperty.getId())) {
                                    ProductPropertyValueDO valueDO = propertyValueMap.get(skuProperty2.getValueId());
                                    availableValueMap.put(skuProperty2.getValueId(), AppProductSkuRespPropertyValueVO.fromProductPropertyValue(valueDO));
                                }
                            }
                        }
                    }
                }
            }

            if (!availableValueMap.isEmpty()) {
                List<AppProductSkuRespPropertyValueVO> availableValues = new ArrayList<>();
                availableValueMap.forEach((k,v) -> availableValues.add(v));
                availableValues.sort(Comparator.comparing(AppProductSkuRespPropertyValueVO::getSort));
                currentProperty.setValues(availableValues);
                availableProperties.add(currentProperty);
            }
        }
        AppProductSkuRespVO resp = new AppProductSkuRespVO(topCategories, subCategories, products);
        resp.setProperties(availableProperties);
        resp.setMinNum(spu.getMinNum());
        resp.setMaxNum(spu.getMaxNum());
        resp.setStepNum(spu.getStepNum());
        resp.setNumLabel(spu.getNumLabel());
        resp.setProxyType(spu.getProxyType());
        resp.setValidateMethod(spu.getValidateMethod());
        resp.setClientLimit(spu.getClientLimit());
        resp.setWhitelistNum(spu.getWhitelistNum());
        resp.setMaxBandwidth(spu.getMaxBandwidth());
        resp.setMaxExtract(spu.getMaxExtract());
        resp.setCanTunnelForwarding(spu.getCanTunnelForwarding());
        resp.setCanSelectCity(spu.getCanSelectCity());

        return success(resp);
    }

    @PostMapping("/get-product-price")
    @Operation(summary = "查询价格")
    @PermitAll
    public CommonResult<AppProductPriceRespVO> getProductSkuPrice(@RequestBody AppProductPriceReqVO reqVO) {
        //其他实体列表
        List<ProductSkuDO> skuList = productSkuService.getSkuListBySpuId(reqVO.getProductId());
        List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList();
        List<ProductPropertyValueDO> propertyValueList = productPropertyValueService.getPropertyValueList();
        ProductSpuDO spu = productSpuService.getSpu(reqVO.getProductId());
        if (spu == null) {
            throw exception(SPU_NOT_EXISTS);
        }

        Map<Long, ProductPropertyDO> propertyMap = new HashMap<>();
        for (ProductPropertyDO propertyDO : propertyList) {
            propertyMap.put(propertyDO.getId(), propertyDO);
        }
        Map<Long, ProductPropertyValueDO> propertyValueMap = new HashMap<>();
        for (ProductPropertyValueDO valueDO : propertyValueList) {
            propertyValueMap.put(valueDO.getId(), valueDO);
        }

        //按请求属性筛选可用sku列表
        for (AppProductPriceReqVO.PriceProperty selectedPriceProperty : reqVO.getProperties()) {
            skuList.removeIf(sku -> {
                for (ProductSkuDO.Property skuProperty : sku.getProperties()) {
                    if (skuProperty.getPropertyId().equals(selectedPriceProperty.getId())) {
                        return !skuProperty.getValueId().equals(selectedPriceProperty.getValueId());
                    }
                }
                return true;
            });
        }
        if (skuList.size() > 1) {
            throw exception(SKU_SELECT_NOT_ONE);
        }
        ProductSkuDO sku = skuList.get(0);
        AppProductPriceRespVO resp = new AppProductPriceRespVO();
        resp.setSkuId(sku.getId());
        resp.setPrice(sku.getPrice() * reqVO.getCount() / spu.getStepNum());
        resp.setMarketPrice(sku.getMarketPrice() * reqVO.getCount() / spu.getStepNum());
        for (ProductSkuDO.Property skuProperty : sku.getProperties()) {
            ProductPropertyDO propertyDO = propertyMap.get(skuProperty.getPropertyId());
            ProductPropertyValueDO valueDO = propertyValueMap.get(skuProperty.getValueId());
            if (StrUtil.isNotEmpty(valueDO.getPromotionMark())) {
                AppProductPriceRespVO.Discount discount = new AppProductPriceRespVO.Discount();
                discount.setPropertyName(propertyDO.getName());
                discount.setPropertyValue(valueDO.getName());
                discount.setPromotion(valueDO.getPromotionMark());
                resp.getDiscounts().add(discount);
            }
        }

        return success(resp);
    }

    @GetMapping("/get-products-in-category")
    @Operation(summary = "获得分类下商品列表")
    @PermitAll
    public CommonResult<List<ProductSpuDO>> getProductsInCategory(Long categoryId) {
        List<ProductSpuDO> spuListInCategory = productSpuService.getSpuListInCategory(categoryId);
        return success(spuListInCategory);
    }

    @GetMapping("/get-product-recommend-skus")
    @Operation(summary = "获得商品推荐SKU数据")
    @PermitAll
    public CommonResult<List<AppProductRecommendSkuRespVO>> getProductRecommendSkus(Long productId) {
        List<AppProductRecommendSkuRespVO> respVOS = new ArrayList<>();
        List<RecommendDO> recommendDOs = recommendService.listBySpuId(productId);
        recommendDOs.forEach(recommendDO -> {
            AppProductRecommendSkuRespVO respVO = new AppProductRecommendSkuRespVO();
            BeanUtils.copyProperties(recommendDO, respVO);
            ProductSkuDO sku = productSkuService.getSku(recommendDO.getSkuId());
            if (sku == null) {
                return;
            }
            int discount = sku.getMarketPrice() - sku.getPrice();
            respVO.setPrice(sku.getPrice());
            respVO.setDiscount(discount);
            respVOS.add(respVO);
        });
        return success(respVOS);
    }
}
